﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using COFE.Core;
using System.Net;

namespace COFE.Web
{
    /// <summary>
    /// Represent a list of Entry.
    /// </summary>
    public abstract class EntryList : SupportLink, IEntryList, ISupportXml
    {
        #region Constructor

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="id">Unspecified = Guid.Empty</param>
        /// <param name="name">Unspecified = null</param>
        protected EntryList(EntryListType type,
            Guid id, string name)
        {
            _type = type;
            _id = id;
            _name = name;
        }

        public static EntryList FromCOFE(COFE.Core.IEntryList entryList)
        {
            return new CoreEntryList(entryList);
        }

        /// <summary>
        /// For updating entry list in the current computer.
        /// </summary>
        /// <param name="host"></param>
        /// <param name="itemXml"></param>
        /// <returns></returns>
        public static IEntryList FromXml(IUserProfile profile, XElement itemXml)
        {
            EntryListType eType; StatusType sType; Guid id; string name, parameter;
            parseXmlAttributes(itemXml, out eType, out id, out name, out parameter, out sType);

            switch (eType)
            {
                case EntryListType.Xml:
                    var entries = from xEle in itemXml.Elements(XNames.Entry)
                                  select FSIEntry.FromXml(profile, xEle, false);
                    return new XmlEntryList(entries, id, name);
                case EntryListType.Auto:
                    COFEShared.CurrentUserProfile.EntryListManager.RemoveEntryList(name);
                    var autoEntryList = COFEShared.CurrentUserProfile.EntryListManager
                        .CreateAutoEntryList(name, parameter);
                    return new CoreEntryList(autoEntryList);
                case EntryListType.Custom:
                    COFEShared.CurrentUserProfile.EntryListManager.RemoveEntryList(name);
                    var customEntryList = COFEShared.CurrentUserProfile.EntryListManager
                        .CreateCustomEntryList(name);
                    customEntryList.AddRange(
                         from xEle in itemXml.Elements(XNames.Entry)
                         let entry =
                         COFEShared.CurrentUserProfile
                         .TryParseGuid(FSIEntry.FromXml(profile, xEle, false).ID)
                         where entry != null
                         select entry);
                    return new CoreEntryList(customEntryList);
                default:
                    throw new NotSupportedException();
            }
        }

        /// <summary>
        /// For reading an entry from entry list in a foreign computer.
        /// </summary>
        /// <param name="host"></param>
        /// <param name="itemXml"></param>
        /// <returns></returns>
        public static IEntryList FromXml(string host, XElement itemXml)
        {
            EntryListType eType; StatusType sType; Guid id; string name, parameter;
            parseXmlAttributes(itemXml, out eType, out id, out name, out parameter, out sType);

            switch (eType)
            {
                case EntryListType.Xml:
                case EntryListType.Auto:
                case EntryListType.Custom:
                    var entries = from xEle in itemXml.Elements(XNames.Entry)
                                  select Entry.FromXElement(host, xEle);
                    return new XmlEntryList(eType, entries, id, name);
                default:
                    throw new NotSupportedException();
            }
        }

        #endregion

        #region Methods

        private static void parseXmlAttributes(XElement itemXml,
            out EntryListType eType,
            out Guid id, out string name, out string parameter,
            out StatusType sType)
        {
            eType = EntryListType.Xml;
            if (itemXml.Attribute(XNames.Type) != null)
                Enum.TryParse<EntryListType>(
                    itemXml.Attribute(XNames.Type).Value,
                    out eType);

            sType = StatusType.Unknown;
            if (itemXml.Attribute(XNames.StatusType) != null)
                sType = StatusTypeExtension.Parse(
                itemXml.Attribute(XNames.StatusType).Value);

            id = Guid.Empty;
            if (itemXml.Attribute(XNames.ID) != null)
                id = new Guid(itemXml.Attribute(XNames.ID).Value);

            name = null;
            if (itemXml.Attribute(XNames.Name) != null)
                name = itemXml.Attribute(XNames.Name).Value;

            parameter = null;
            if (itemXml.Attribute(XNames.Parameter) != null)
                parameter = itemXml.Attribute(XNames.Parameter).Value;
        }

        /// <summary>
        /// Serialize an entry list.
        /// </summary>
        /// <returns></returns>
        public XElement ToXml(bool indentLinks = false)
        {
            var entryList = new XElement(XNames.List,
                                 new XAttribute(XNames.StatusType, Status.ToString()),
                                 new XAttribute(XNames.Type, Type.ToString())
                                 );

            if (Id != Guid.Empty)
                entryList.Add(new XAttribute(XNames.ID, Id.ToString()));
            if (Name != null)
                entryList.Add(new XAttribute(XNames.Name, Name));
            if (Parameter != null)
                entryList.Add(new XAttribute(XNames.Parameter, Parameter));

            var entryRoot = new XElement(XNames.Entries);
            entryList.Add(entryRoot);

            foreach (var entry in getEntries())
                entryRoot.Add(entry.ToXml(indentLinks));            
            writeLinks(entryList, indentLinks);

            return entryList;
        }

        protected abstract IEnumerable<Entry> getEntries();        
        protected abstract StatusType getStatus();

        public override string ToString()
        {
            return GetHashCode().ToString();
        }

        public override bool Equals(object obj)
        {
            return (obj is EntryListBase) && GetHashCode() == obj.GetHashCode();
        }

        public override int GetHashCode()
        {
            int hashCode = 0;
            foreach (var entry in getEntries())
                hashCode = (hashCode + entry.GetHashCode()) / 2;
            hashCode += Status.GetHashCode();

            return hashCode;
        }

        #endregion

        #region Data

        private Guid _id = Guid.Empty;
        private string _name = null;
        private string _parameter = null;
        private EntryListType _type;

        #endregion

        #region Public Properties

        public Guid Id { get { return _id; } }
        public string Name { get { return _name; } }
        public EntryListType Type { get { return _type; } }
        public string Parameter { get { return _parameter; } protected set { _parameter = value; } }
        public IEnumerable<Entry> Entries { get { return getEntries(); } }        
        public StatusType Status { get { return getStatus(); } }

        #endregion
    }
}
